
#import "UIScrollView+EmptyDataSet.h"

#import <objc/runtime.h>

@interface UIView (DZNConstraintBasedLayoutExtensions)

- (NSLayoutConstraint *)equallyRelatedConstraintWithView:(UIView *)view attribute:(NSLayoutAttribute)attribute;

@end

@interface DZNWeakObjectContainer : NSObject

@property (nonatomic, readonly, weak) id weakObject;

- (instancetype)initWithWeakObject:(id)object;

@end

@interface DZNEmptyDataSetView : UIView

@property (nonatomic, readonly) UIView *loveLateAppContentView;
@property (nonatomic, readonly) UILabel *loveLateAppTitleLabel;
@property (nonatomic, readonly) UILabel *loveLateAppDetailLabel;
@property (nonatomic, readonly) UIImageView *loveLateAppImageView;
@property (nonatomic, readonly) UIButton *button;
@property (nonatomic, strong) UIView *loveLateAppCustomView;
@property (nonatomic, strong) UITapGestureRecognizer *loveLateAppTapGesture;

@property (nonatomic, assign) CGFloat loveLateAppVerticalOffset;
@property (nonatomic, assign) CGFloat loveLateAppVerticalSpace;

@property (nonatomic, assign) BOOL fadeInOnDisplay;

- (void)setupConstraints;
- (void)prepareForReuse;

@end


#pragma mark - UIScrollView+EmptyDataSet

static char const * const kEmptyDataSetSource =     "emptyDataSetSource";
static char const * const kEmptyDataSetDelegate =   "emptyDataSetDelegate";
static char const * const kEmptyDataSetView =       "emptyDataSetView";

#define kEmptyImageViewAnimationKey @"com.dzn.emptyDataSet.imageViewAnimation"

@interface UIScrollView () <UIGestureRecognizerDelegate>
@property (nonatomic, readonly) DZNEmptyDataSetView *emptyDataSetView;
@end

@implementation UIScrollView (DZNEmptyDataSet)

#pragma mark - Getters (Public)

- (id<DZNEmptyDataSetSource>)emptyDataSetSource
{
    DZNWeakObjectContainer *loveLateAppContainer = objc_getAssociatedObject(self, kEmptyDataSetSource);
    return loveLateAppContainer.weakObject;
}

- (id<DZNEmptyDataSetDelegate>)emptyDataSetDelegate
{
    DZNWeakObjectContainer *loveLateAppContainer = objc_getAssociatedObject(self, kEmptyDataSetDelegate);
    return loveLateAppContainer.weakObject;
}

- (BOOL)isEmptyDataSetVisible
{
    UIView *loveLateAppView = objc_getAssociatedObject(self, kEmptyDataSetView);
    return loveLateAppView ? !loveLateAppView.hidden : NO;
}


#pragma mark - Getters (Private)

- (DZNEmptyDataSetView *)emptyDataSetView
{
    DZNEmptyDataSetView *loveLateAppView = objc_getAssociatedObject(self, kEmptyDataSetView);
    
    if (!loveLateAppView) {
        loveLateAppView = [DZNEmptyDataSetView new];
        loveLateAppView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
        loveLateAppView.hidden = YES;
        
        loveLateAppView.loveLateAppTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dzn_didTapContentView:)];
        loveLateAppView.loveLateAppTapGesture.delegate = self;
        [loveLateAppView addGestureRecognizer:loveLateAppView.loveLateAppTapGesture];
        
        [self setEmptyDataSetView:loveLateAppView];
    }
    return loveLateAppView;
}

- (BOOL)dzn_canDisplay
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource conformsToProtocol:@protocol(DZNEmptyDataSetSource)]) {
        if ([self isKindOfClass:[UITableView class]] || [self isKindOfClass:[UICollectionView class]] || [self isKindOfClass:[UIScrollView class]]) {
            return YES;
        }
    }
    
    return NO;
}

- (NSInteger)dzn_itemsCount
{
    NSInteger loveLateAppItems = 0;
    
    if (![self respondsToSelector:@selector(dataSource)]) {
        return loveLateAppItems;
    }
    
    if ([self isKindOfClass:[UITableView class]]) {
        
        UITableView *loveLateAppTableView = (UITableView *)self;
        id <UITableViewDataSource> loveLateAppDataSource = loveLateAppTableView.dataSource;
        
        NSInteger loveLateAppSections = 1;
        
        if (loveLateAppDataSource && [loveLateAppDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
            loveLateAppSections = [loveLateAppDataSource numberOfSectionsInTableView:loveLateAppTableView];
        }
        
        if (loveLateAppDataSource && [loveLateAppDataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) {
            for (NSInteger loveLateAppSection = 0; loveLateAppSection < loveLateAppSections; loveLateAppSection++) {
                loveLateAppItems += [loveLateAppDataSource tableView:loveLateAppTableView numberOfRowsInSection:loveLateAppSection];
            }
        }
    }
    
    else if ([self isKindOfClass:[UICollectionView class]]) {
        
        UICollectionView *loveLateAppCollectionView = (UICollectionView *)self;
        id <UICollectionViewDataSource> loveLateAppDataSource = loveLateAppCollectionView.dataSource;

        NSInteger sections = 1;
        
        if (loveLateAppDataSource && [loveLateAppDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) {
            sections = [loveLateAppDataSource numberOfSectionsInCollectionView:loveLateAppCollectionView];
        }
        
        if (loveLateAppDataSource && [loveLateAppDataSource respondsToSelector:@selector(collectionView:numberOfItemsInSection:)]) {
            for (NSInteger loveLateAppSection = 0; loveLateAppSection < sections; loveLateAppSection++) {
                loveLateAppItems += [loveLateAppDataSource collectionView:loveLateAppCollectionView numberOfItemsInSection:loveLateAppSection];
            }
        }
    }
    
    return loveLateAppItems;
}


#pragma mark - Data Source Getters

- (NSAttributedString *)dzn_titleLabelString
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(titleForEmptyDataSet:)]) {
        NSAttributedString *loveLateAppString = [self.emptyDataSetSource titleForEmptyDataSet:self];
        if (loveLateAppString) NSAssert([loveLateAppString isKindOfClass:[NSAttributedString class]], @"You must return a valid NSAttributedString object for -titleForEmptyDataSet:");
        return loveLateAppString;
    }
    return nil;
}

- (NSAttributedString *)dzn_detailLabelString
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(descriptionForEmptyDataSet:)]) {
        NSAttributedString *loveLateAppString = [self.emptyDataSetSource descriptionForEmptyDataSet:self];
        if (loveLateAppString) NSAssert([loveLateAppString isKindOfClass:[NSAttributedString class]], @"You must return a valid NSAttributedString object for -descriptionForEmptyDataSet:");
        return loveLateAppString;
    }
    return nil;
}

- (UIImage *)dzn_image
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(imageForEmptyDataSet:)]) {
        UIImage *loveLateAppImage = [self.emptyDataSetSource imageForEmptyDataSet:self];
        if (loveLateAppImage) NSAssert([loveLateAppImage isKindOfClass:[UIImage class]], @"You must return a valid UIImage object for -imageForEmptyDataSet:");
        return loveLateAppImage;
    }
    return nil;
}

- (CAAnimation *)dzn_imageAnimation
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(imageAnimationForEmptyDataSet:)]) {
        CAAnimation *loveLateAppImageAnimation = [self.emptyDataSetSource imageAnimationForEmptyDataSet:self];
        if (loveLateAppImageAnimation) NSAssert([loveLateAppImageAnimation isKindOfClass:[CAAnimation class]], @"You must return a valid CAAnimation object for -imageAnimationForEmptyDataSet:");
        return loveLateAppImageAnimation;
    }
    return nil;
}

- (UIColor *)dzn_imageTintColor
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(imageTintColorForEmptyDataSet:)]) {
        UIColor *loveLateAppColor = [self.emptyDataSetSource imageTintColorForEmptyDataSet:self];
        if (loveLateAppColor) NSAssert([loveLateAppColor isKindOfClass:[UIColor class]], @"You must return a valid UIColor object for -imageTintColorForEmptyDataSet:");
        return loveLateAppColor;
    }
    return nil;
}

- (NSAttributedString *)dzn_buttonTitleForState:(UIControlState)state
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(buttonTitleForEmptyDataSet:forState:)]) {
        NSAttributedString *loveLateAppString = [self.emptyDataSetSource buttonTitleForEmptyDataSet:self forState:state];
        if (loveLateAppString) NSAssert([loveLateAppString isKindOfClass:[NSAttributedString class]], @"You must return a valid NSAttributedString object for -buttonTitleForEmptyDataSet:forState:");
        return loveLateAppString;
    }
    return nil;
}

- (UIImage *)dzn_buttonImageForState:(UIControlState)state
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(buttonImageForEmptyDataSet:forState:)]) {
        UIImage *loveLateAppUmage = [self.emptyDataSetSource buttonImageForEmptyDataSet:self forState:state];
        if (loveLateAppUmage) NSAssert([loveLateAppUmage isKindOfClass:[UIImage class]], @"You must return a valid UIImage object for -buttonImageForEmptyDataSet:forState:");
        return loveLateAppUmage;
    }
    return nil;
}

- (UIImage *)dzn_buttonBackgroundImageForState:(UIControlState)state
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(buttonBackgroundImageForEmptyDataSet:forState:)]) {
        UIImage *loveLateAppImage = [self.emptyDataSetSource buttonBackgroundImageForEmptyDataSet:self forState:state];
        if (loveLateAppImage) NSAssert([loveLateAppImage isKindOfClass:[UIImage class]], @"You must return a valid UIImage object for -buttonBackgroundImageForEmptyDataSet:forState:");
        return loveLateAppImage;
    }
    return nil;
}

- (UIColor *)dzn_dataSetBackgroundColor
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(backgroundColorForEmptyDataSet:)]) {
        UIColor *loveLateAppColor = [self.emptyDataSetSource backgroundColorForEmptyDataSet:self];
        if (loveLateAppColor) NSAssert([loveLateAppColor isKindOfClass:[UIColor class]], @"You must return a valid UIColor object for -backgroundColorForEmptyDataSet:");
        return loveLateAppColor;
    }
    return [UIColor clearColor];
}

- (UIView *)dzn_customView
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(customViewForEmptyDataSet:)]) {
        UIView *loveLateAppView = [self.emptyDataSetSource customViewForEmptyDataSet:self];
        if (loveLateAppView) NSAssert([loveLateAppView isKindOfClass:[UIView class]], @"You must return a valid UIView object for -customViewForEmptyDataSet:");
        return loveLateAppView;
    }
    return nil;
}

- (CGFloat)dzn_verticalOffset
{
    CGFloat loveLateAppOffset = 0.0;
    
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(verticalOffsetForEmptyDataSet:)]) {
        loveLateAppOffset = [self.emptyDataSetSource verticalOffsetForEmptyDataSet:self];
    }
    return loveLateAppOffset;
}

- (CGFloat)dzn_verticalSpace
{
    if (self.emptyDataSetSource && [self.emptyDataSetSource respondsToSelector:@selector(spaceHeightForEmptyDataSet:)]) {
        return [self.emptyDataSetSource spaceHeightForEmptyDataSet:self];
    }
    return 0.0;
}


#pragma mark - Delegate Getters & Events (Private)

- (BOOL)dzn_shouldFadeIn {
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetShouldFadeIn:)]) {
        return [self.emptyDataSetDelegate emptyDataSetShouldFadeIn:self];
    }
    return YES;
}

- (BOOL)dzn_shouldDisplay
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetShouldDisplay:)]) {
        return [self.emptyDataSetDelegate emptyDataSetShouldDisplay:self];
    }
    return YES;
}

- (BOOL)dzn_shouldBeForcedToDisplay
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetShouldBeForcedToDisplay:)]) {
        return [self.emptyDataSetDelegate emptyDataSetShouldBeForcedToDisplay:self];
    }
    return NO;
}

- (BOOL)dzn_isTouchAllowed
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetShouldAllowTouch:)]) {
        return [self.emptyDataSetDelegate emptyDataSetShouldAllowTouch:self];
    }
    return YES;
}

- (BOOL)dzn_isScrollAllowed
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetShouldAllowScroll:)]) {
        return [self.emptyDataSetDelegate emptyDataSetShouldAllowScroll:self];
    }
    return NO;
}

- (BOOL)dzn_isImageViewAnimateAllowed
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetShouldAnimateImageView:)]) {
        return [self.emptyDataSetDelegate emptyDataSetShouldAnimateImageView:self];
    }
    return NO;
}

- (void)dzn_willAppear
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetWillAppear:)]) {
        [self.emptyDataSetDelegate emptyDataSetWillAppear:self];
    }
}

- (void)dzn_didAppear
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetDidAppear:)]) {
        [self.emptyDataSetDelegate emptyDataSetDidAppear:self];
    }
}

- (void)dzn_willDisappear
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetWillDisappear:)]) {
        [self.emptyDataSetDelegate emptyDataSetWillDisappear:self];
    }
}

- (void)dzn_didDisappear
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetDidDisappear:)]) {
        [self.emptyDataSetDelegate emptyDataSetDidDisappear:self];
    }
}

- (void)dzn_didTapContentView:(id)sender
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSet:didTapView:)]) {
        [self.emptyDataSetDelegate emptyDataSet:self didTapView:sender];
    }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    else if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetDidTapView:)]) {
        [self.emptyDataSetDelegate emptyDataSetDidTapView:self];
    }
#pragma clang diagnostic pop
}

- (void)dzn_didTapDataButton:(id)sender
{
    if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSet:didTapButton:)]) {
        [self.emptyDataSetDelegate emptyDataSet:self didTapButton:sender];
    }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    else if (self.emptyDataSetDelegate && [self.emptyDataSetDelegate respondsToSelector:@selector(emptyDataSetDidTapButton:)]) {
        [self.emptyDataSetDelegate emptyDataSetDidTapButton:self];
    }
#pragma clang diagnostic pop
}


#pragma mark - Setters (Public)

- (void)setEmptyDataSetSource:(id<DZNEmptyDataSetSource>)datasource {
    if (!datasource || ![self dzn_canDisplay]) {
        [self dzn_invalidate];
    }
    
    objc_setAssociatedObject(self, kEmptyDataSetSource, [[DZNWeakObjectContainer alloc] initWithWeakObject:datasource], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    [self swizzleIfPossible:@selector(reloadData)];
    
    if ([self isKindOfClass:[UITableView class]]) {
        [self swizzleIfPossible:@selector(endUpdates)];
    }
}

- (void)setEmptyDataSetDelegate:(id<DZNEmptyDataSetDelegate>)delegate
{
    if (!delegate) {
        [self dzn_invalidate];
    }
    
    objc_setAssociatedObject(self, kEmptyDataSetDelegate, [[DZNWeakObjectContainer alloc] initWithWeakObject:delegate], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


#pragma mark - Setters (Private)

- (void)setEmptyDataSetView:(DZNEmptyDataSetView *)view
{
    objc_setAssociatedObject(self, kEmptyDataSetView, view, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


#pragma mark - Reload APIs (Public)

- (void)reloadEmptyDataSet {
    [self dzn_reloadEmptyDataSet];
}


#pragma mark - Reload APIs (Private)

- (void)dzn_reloadEmptyDataSet {
    if (![self dzn_canDisplay]) {
        return;
    }
    
    if (([self dzn_shouldDisplay] && [self dzn_itemsCount] == 0) || [self dzn_shouldBeForcedToDisplay]) {
        [self dzn_willAppear];
        
        DZNEmptyDataSetView *loveLateAppView = self.emptyDataSetView;
        
        loveLateAppView.fadeInOnDisplay = [self dzn_shouldFadeIn];
        
        if (!loveLateAppView.superview) {
            if (([self isKindOfClass:[UITableView class]] || [self isKindOfClass:[UICollectionView class]]) && self.subviews.count > 1) {
                [self insertSubview:loveLateAppView atIndex:0];
            }
            else {
                [self addSubview:loveLateAppView];
            }
        }
        
        [loveLateAppView prepareForReuse];
        
        UIView *loveLateAppCustomView = [self dzn_customView];
        
        if (loveLateAppCustomView) {
            loveLateAppView.loveLateAppCustomView = loveLateAppCustomView;
        }
        else {
            NSAttributedString *titleLabelString = [self dzn_titleLabelString];
            NSAttributedString *detailLabelString = [self dzn_detailLabelString];
            
            UIImage *buttonImage = [self dzn_buttonImageForState:UIControlStateNormal];
            NSAttributedString *buttonTitle = [self dzn_buttonTitleForState:UIControlStateNormal];
            
            UIImage *image = [self dzn_image];
            UIColor *imageTintColor = [self dzn_imageTintColor];
            UIImageRenderingMode renderingMode = imageTintColor ? UIImageRenderingModeAlwaysTemplate : UIImageRenderingModeAlwaysOriginal;
            
            loveLateAppView.loveLateAppVerticalSpace = [self dzn_verticalSpace];
            
            if (image) {
                if ([image respondsToSelector:@selector(imageWithRenderingMode:)]) {
                    loveLateAppView.loveLateAppImageView.image = [image imageWithRenderingMode:renderingMode];
                    loveLateAppView.loveLateAppImageView.tintColor = imageTintColor;
                }
                else {
                    loveLateAppView.loveLateAppImageView.image = image;
                }
            }
            
            if (titleLabelString) {
                loveLateAppView.loveLateAppTitleLabel.attributedText = titleLabelString;
            }
            
            if (detailLabelString) {
                loveLateAppView.loveLateAppDetailLabel.attributedText = detailLabelString;
            }
            
            if (buttonImage) {
                [loveLateAppView.button setImage:buttonImage forState:UIControlStateNormal];
                [loveLateAppView.button setImage:[self dzn_buttonImageForState:UIControlStateHighlighted] forState:UIControlStateHighlighted];
            }
            else if (buttonTitle) {
                [loveLateAppView.button setAttributedTitle:buttonTitle forState:UIControlStateNormal];
                [loveLateAppView.button setAttributedTitle:[self dzn_buttonTitleForState:UIControlStateHighlighted] forState:UIControlStateHighlighted];
                [loveLateAppView.button setBackgroundImage:[self dzn_buttonBackgroundImageForState:UIControlStateNormal] forState:UIControlStateNormal];
                [loveLateAppView.button setBackgroundImage:[self dzn_buttonBackgroundImageForState:UIControlStateHighlighted] forState:UIControlStateHighlighted];
            }
        }
        
        loveLateAppView.loveLateAppVerticalOffset = [self dzn_verticalOffset];
        
        loveLateAppView.backgroundColor = [self dzn_dataSetBackgroundColor];
        loveLateAppView.hidden = NO;
        loveLateAppView.clipsToBounds = YES;
        
        loveLateAppView.userInteractionEnabled = [self dzn_isTouchAllowed];
        
        [loveLateAppView setupConstraints];
        
        [UIView performWithoutAnimation:^{
            [loveLateAppView layoutIfNeeded];
        }];
        
        self.scrollEnabled = [self dzn_isScrollAllowed];
        
        if ([self dzn_isImageViewAnimateAllowed])
        {
            CAAnimation *animation = [self dzn_imageAnimation];
            
            if (animation) {
                [self.emptyDataSetView.loveLateAppImageView.layer addAnimation:animation forKey:kEmptyImageViewAnimationKey];
            }
        }
        else if ([self.emptyDataSetView.loveLateAppImageView.layer animationForKey:kEmptyImageViewAnimationKey]) {
            [self.emptyDataSetView.loveLateAppImageView.layer removeAnimationForKey:kEmptyImageViewAnimationKey];
        }
        
        [self dzn_didAppear];
    }
    else if (self.isEmptyDataSetVisible) {
        [self dzn_invalidate];
    }
}

- (void)dzn_invalidate {
    [self dzn_willDisappear];
    
    if (self.emptyDataSetView) {
        [self.emptyDataSetView prepareForReuse];
        [self.emptyDataSetView removeFromSuperview];
        
        [self setEmptyDataSetView:nil];
    }
    
    self.scrollEnabled = YES;
    
    [self dzn_didDisappear];
}


#pragma mark - Method Swizzling

static NSMutableDictionary *_impLookupTable;
static NSString *const DZNSwizzleInfoPointerKey = @"pointer";
static NSString *const DZNSwizzleInfoOwnerKey = @"owner";
static NSString *const DZNSwizzleInfoSelectorKey = @"selector";


void dzn_original_implementation(id self, SEL _cmd)
{
    Class loveLateAppBaseClass = dzn_baseClassToSwizzleForTarget(self);
    NSString *loveLateAppKey = dzn_implementationKey(loveLateAppBaseClass, _cmd);
    
    NSDictionary *loveLateAppSwizzleInfo = [_impLookupTable objectForKey:loveLateAppKey];
    NSValue *loveLateAppImpValue = [loveLateAppSwizzleInfo valueForKey:DZNSwizzleInfoPointerKey];
    
    IMP loveLateAppImpPointer = [loveLateAppImpValue pointerValue];
    
    [self dzn_reloadEmptyDataSet];
    
    if (loveLateAppImpPointer) {
        ((void(*)(id,SEL))loveLateAppImpPointer)(self,_cmd);
    }
}

NSString *dzn_implementationKey(Class class, SEL selector) {
    if (!class || !selector) {
        return nil;
    }
    
    NSString *loveLateAppClassName = NSStringFromClass([class class]);
    NSString *loveLateAppSelectorName = NSStringFromSelector(selector);
    
    return [NSString stringWithFormat:@"%@_%@",loveLateAppClassName,loveLateAppSelectorName];
}

Class dzn_baseClassToSwizzleForTarget(id target)
{
    if ([target isKindOfClass:[UITableView class]]) {
        return [UITableView class];
    }
    else if ([target isKindOfClass:[UICollectionView class]]) {
        return [UICollectionView class];
    }
    else if ([target isKindOfClass:[UIScrollView class]]) {
        return [UIScrollView class];
    }
    
    return nil;
}

- (void)swizzleIfPossible:(SEL)selector {
    if (![self respondsToSelector:selector]) {
        return;
    }
    
    if (!_impLookupTable) {
        _impLookupTable = [[NSMutableDictionary alloc] initWithCapacity:3];
    }
    
    for (NSDictionary *loveLateAppInfo in [_impLookupTable allValues]) {
        Class loveLateAppClass = [loveLateAppInfo objectForKey:DZNSwizzleInfoOwnerKey];
        NSString *loveLateAppSelectorName = [loveLateAppInfo objectForKey:DZNSwizzleInfoSelectorKey];
        
        if ([loveLateAppSelectorName isEqualToString:NSStringFromSelector(selector)]) {
            if ([self isKindOfClass:loveLateAppClass]) {
                return;
            }
        }
    }
    
    Class loveLateAppBaseClass = dzn_baseClassToSwizzleForTarget(self);
    NSString *loveLateAppKey = dzn_implementationKey(loveLateAppBaseClass, selector);
    NSValue *loveLateAppImpValue = [[_impLookupTable objectForKey:loveLateAppKey] valueForKey:DZNSwizzleInfoPointerKey];
    
    if (loveLateAppImpValue || !loveLateAppKey || !loveLateAppBaseClass) {
        return;
    }
    
    Method loveLateAppMethod = class_getInstanceMethod(loveLateAppBaseClass, selector);
    IMP dzn_newImplementation = method_setImplementation(loveLateAppMethod, (IMP)dzn_original_implementation);
    
    NSDictionary *loveLateAppSwizzledInfo = @{DZNSwizzleInfoOwnerKey: loveLateAppBaseClass,
                                   DZNSwizzleInfoSelectorKey: NSStringFromSelector(selector),
                                   DZNSwizzleInfoPointerKey: [NSValue valueWithPointer:dzn_newImplementation]};
    
    [_impLookupTable setObject:loveLateAppSwizzledInfo forKey:loveLateAppKey];
}


#pragma mark - UIGestureRecognizerDelegate Methods

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if ([gestureRecognizer.view isEqual:self.emptyDataSetView]) {
        return [self dzn_isTouchAllowed];
    }
    return [super gestureRecognizerShouldBegin:gestureRecognizer];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    UIGestureRecognizer *loveLateAppTapGesture = self.emptyDataSetView.loveLateAppTapGesture;
    
    if ([gestureRecognizer isEqual:loveLateAppTapGesture] || [otherGestureRecognizer isEqual:loveLateAppTapGesture]) {
        return YES;
    }
    
    if ( (self.emptyDataSetDelegate != (id)self) && [self.emptyDataSetDelegate respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
        return [(id)self.emptyDataSetDelegate gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
    }
    
    return NO;
}

@end


#pragma mark - DZNEmptyDataSetView

@interface DZNEmptyDataSetView ()
@end

@implementation DZNEmptyDataSetView
@synthesize loveLateAppContentView = _loveLateAppContentView;
@synthesize loveLateAppTitleLabel = _loveLateAppTitleLabel, loveLateAppDetailLabel = _loveLateAppDetailLabel, loveLateAppImageView = _loveLateAppImageView, button = _button;

#pragma mark - Initialization Methods

- (instancetype)init
{
    self =  [super init];
    if (self) {
        [self addSubview:self.loveLateAppContentView];
    }
    return self;
}

- (void)didMoveToSuperview {
    CGRect loveLateAppSuperviewBounds = self.superview.bounds;
    self.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(loveLateAppSuperviewBounds), CGRectGetHeight(loveLateAppSuperviewBounds));
    
    void(^fadeInBlock)(void) = ^{self->_loveLateAppContentView.alpha = 1.0;};
    
    if (self.fadeInOnDisplay) {
        [UIView animateWithDuration:0.25
                         animations:fadeInBlock
                         completion:NULL];
    }
    else {
        fadeInBlock();
    }
}


#pragma mark - Getters

- (UIView *)loveLateAppContentView
{
    if (!_loveLateAppContentView)
    {
        _loveLateAppContentView = [UIView new];
        _loveLateAppContentView.translatesAutoresizingMaskIntoConstraints = NO;
        _loveLateAppContentView.backgroundColor = [UIColor clearColor];
        _loveLateAppContentView.userInteractionEnabled = YES;
        _loveLateAppContentView.alpha = 0;
    }
    return _loveLateAppContentView;
}

- (UIImageView *)loveLateAppImageView
{
    if (!_loveLateAppImageView)
    {
        _loveLateAppImageView = [UIImageView new];
        _loveLateAppImageView.translatesAutoresizingMaskIntoConstraints = NO;
        _loveLateAppImageView.backgroundColor = [UIColor clearColor];
        _loveLateAppImageView.contentMode = UIViewContentModeScaleAspectFit;
        _loveLateAppImageView.userInteractionEnabled = NO;
        _loveLateAppImageView.accessibilityIdentifier = @"empty set background image";
        
        [_loveLateAppContentView addSubview:_loveLateAppImageView];
    }
    return _loveLateAppImageView;
}

- (UILabel *)loveLateAppTitleLabel
{
    if (!_loveLateAppTitleLabel)
    {
        _loveLateAppTitleLabel = [UILabel new];
        _loveLateAppTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
        _loveLateAppTitleLabel.backgroundColor = [UIColor clearColor];
        
        _loveLateAppTitleLabel.font = [UIFont systemFontOfSize:27.0];
        _loveLateAppTitleLabel.textColor = [UIColor colorWithWhite:0.6 alpha:1.0];
        _loveLateAppTitleLabel.textAlignment = NSTextAlignmentCenter;
        _loveLateAppTitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
        _loveLateAppTitleLabel.numberOfLines = 0;
        _loveLateAppTitleLabel.accessibilityIdentifier = @"empty set title";
        
        [_loveLateAppContentView addSubview:_loveLateAppTitleLabel];
    }
    return _loveLateAppTitleLabel;
}

- (UILabel *)loveLateAppDetailLabel
{
    if (!_loveLateAppDetailLabel)
    {
        _loveLateAppDetailLabel = [UILabel new];
        _loveLateAppDetailLabel.translatesAutoresizingMaskIntoConstraints = NO;
        _loveLateAppDetailLabel.backgroundColor = [UIColor clearColor];
        
        _loveLateAppDetailLabel.font = [UIFont systemFontOfSize:17.0];
        _loveLateAppDetailLabel.textColor = [UIColor colorWithWhite:0.6 alpha:1.0];
        _loveLateAppDetailLabel.textAlignment = NSTextAlignmentCenter;
        _loveLateAppDetailLabel.lineBreakMode = NSLineBreakByWordWrapping;
        _loveLateAppDetailLabel.numberOfLines = 0;
        _loveLateAppDetailLabel.accessibilityIdentifier = @"empty set detail label";
        
        [_loveLateAppContentView addSubview:_loveLateAppDetailLabel];
    }
    return _loveLateAppDetailLabel;
}

- (UIButton *)button
{
    if (!_button)
    {
        _button = [UIButton buttonWithType:UIButtonTypeCustom];
        _button.translatesAutoresizingMaskIntoConstraints = NO;
        _button.backgroundColor = [UIColor clearColor];
        _button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
        _button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
        _button.accessibilityIdentifier = @"empty set button";
        
        [_button addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];
        
        [_loveLateAppContentView addSubview:_button];
    }
    return _button;
}

- (BOOL)canShowImage
{
    return (_loveLateAppImageView.image && _loveLateAppImageView.superview);
}

- (BOOL)canShowTitle
{
    return (_loveLateAppTitleLabel.attributedText.string.length > 0 && _loveLateAppTitleLabel.superview);
}

- (BOOL)canShowDetail
{
    return (_loveLateAppDetailLabel.attributedText.string.length > 0 && _loveLateAppDetailLabel.superview);
}

- (BOOL)canShowButton
{
    if ([_button attributedTitleForState:UIControlStateNormal].string.length > 0 || [_button imageForState:UIControlStateNormal]) {
        return (_button.superview != nil);
    }
    return NO;
}


#pragma mark - Setters

- (void)setLoveLateAppCustomView:(UIView *)view
{
    if (!view) {
        return;
    }
    
    if (_loveLateAppCustomView) {
        [_loveLateAppCustomView removeFromSuperview];
        _loveLateAppCustomView = nil;
    }
    
    _loveLateAppCustomView = view;
    _loveLateAppCustomView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.loveLateAppContentView addSubview:_loveLateAppCustomView];
}


#pragma mark - Action Methods

- (void)didTapButton:(id)sender
{
    SEL selector = NSSelectorFromString(@"dzn_didTapDataButton:");
    
    if ([self.superview respondsToSelector:selector]) {
        [self.superview performSelector:selector withObject:sender afterDelay:0.0f];
    }
}

- (void)removeAllConstraints {
    [self removeConstraints:self.constraints];
    [_loveLateAppContentView removeConstraints:_loveLateAppContentView.constraints];
}

- (void)prepareForReuse {
    [self.loveLateAppContentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    
    _loveLateAppTitleLabel = nil;
    _loveLateAppDetailLabel = nil;
    _loveLateAppImageView = nil;
    _button = nil;
    _loveLateAppCustomView = nil;
    
    [self removeAllConstraints];
}


#pragma mark - Auto-Layout Configuration

- (void)setupConstraints {
    NSLayoutConstraint *loveLateAppCenterXConstraint = [self equallyRelatedConstraintWithView:self.loveLateAppContentView attribute:NSLayoutAttributeCenterX];
    NSLayoutConstraint *loveLateAppCenterYConstraint = [self equallyRelatedConstraintWithView:self.loveLateAppContentView attribute:NSLayoutAttributeCenterY];
    
    [self addConstraint:loveLateAppCenterXConstraint];
    [self addConstraint:loveLateAppCenterYConstraint];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:nil views:@{@"contentView": self.loveLateAppContentView}]];
    
    if (self.loveLateAppVerticalOffset != 0 && self.constraints.count > 0) {
        loveLateAppCenterYConstraint.constant = self.loveLateAppVerticalOffset;
    }
    
    if (_loveLateAppCustomView) {
        [self.loveLateAppContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[customView]|" options:0 metrics:nil views:@{@"customView":_loveLateAppCustomView}]];
        [self.loveLateAppContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|" options:0 metrics:nil views:@{@"customView":_loveLateAppCustomView}]];
    }
    else {
        CGFloat loveLateAppWidth = CGRectGetWidth(self.frame) ? : CGRectGetWidth([UIScreen mainScreen].bounds);
        CGFloat loveLateAppPadding = roundf(loveLateAppWidth/16.0);
        CGFloat loveLateAppVerticalSpace = self.loveLateAppVerticalSpace ? : 11.0;
        
        NSMutableArray *loveLateAppSubviewStrings = [NSMutableArray array];
        NSMutableDictionary *loveLateAppViews = [NSMutableDictionary dictionary];
        NSDictionary *loveLateAppMetrics = @{@"padding": @(loveLateAppPadding)};
        
        if (_loveLateAppImageView.superview) {
    
            [loveLateAppSubviewStrings addObject:@"imageView"];
            loveLateAppViews[[loveLateAppSubviewStrings lastObject]] = _loveLateAppImageView;
            
            [self.loveLateAppContentView addConstraint:[self.loveLateAppContentView equallyRelatedConstraintWithView:_loveLateAppImageView attribute:NSLayoutAttributeCenterX]];
        }
        
        if ([self canShowTitle]) {
            [loveLateAppSubviewStrings addObject:@"titleLabel"];
            loveLateAppViews[[loveLateAppSubviewStrings lastObject]] = _loveLateAppTitleLabel;
            
            [self.loveLateAppContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(padding@750)-[titleLabel(>=0)]-(padding@750)-|"
                                                                                     options:0 metrics:loveLateAppMetrics views:loveLateAppViews]];
        }
        else {
            [_loveLateAppTitleLabel removeFromSuperview];
            _loveLateAppTitleLabel = nil;
        }
        
        if ([self canShowDetail]) {
            
            [loveLateAppSubviewStrings addObject:@"detailLabel"];
            loveLateAppViews[[loveLateAppSubviewStrings lastObject]] = _loveLateAppDetailLabel;
            
            [self.loveLateAppContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(padding@750)-[detailLabel(>=0)]-(padding@750)-|"
                                                                                     options:0 metrics:loveLateAppMetrics views:loveLateAppViews]];
        }
        else {
            [_loveLateAppDetailLabel removeFromSuperview];
            _loveLateAppDetailLabel = nil;
        }
        
        if ([self canShowButton]) {
            
            [loveLateAppSubviewStrings addObject:@"button"];
            loveLateAppViews[[loveLateAppSubviewStrings lastObject]] = _button;
            
            [self.loveLateAppContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(padding@750)-[button(>=0)]-(padding@750)-|"
                                                                                     options:0 metrics:loveLateAppMetrics views:loveLateAppViews]];
        }
        else {
            [_button removeFromSuperview];
            _button = nil;
        }
        
        NSMutableString *loveLateAppVerticalFormat = [NSMutableString new];
        
        for (int loveLateAppIdx = 0; loveLateAppIdx < loveLateAppSubviewStrings.count; loveLateAppIdx++) {
            
            NSString *loveLateAppString = loveLateAppSubviewStrings[loveLateAppIdx];
            [loveLateAppVerticalFormat appendFormat:@"[%@]", loveLateAppString];
            
            if (loveLateAppIdx < loveLateAppSubviewStrings.count-1) {
                [loveLateAppVerticalFormat appendFormat:@"-(%.f@750)-", loveLateAppVerticalSpace];
            }
        }
        
        if (loveLateAppVerticalFormat.length > 0) {
            [self.loveLateAppContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|%@|", loveLateAppVerticalFormat]
                                                                                     options:0 metrics:loveLateAppMetrics views:loveLateAppViews]];
        }
    }
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *loveLateAppHitView = [super hitTest:point withEvent:event];
    
    if ([loveLateAppHitView isKindOfClass:[UIControl class]]) {
        return loveLateAppHitView;
    }
    
    if ([loveLateAppHitView isEqual:_loveLateAppContentView] || [loveLateAppHitView isEqual:_loveLateAppCustomView]) {
        return loveLateAppHitView;
    }
    return nil;
}

@end


#pragma mark - UIView+DZNConstraintBasedLayoutExtensions

@implementation UIView (DZNConstraintBasedLayoutExtensions)

- (NSLayoutConstraint *)equallyRelatedConstraintWithView:(UIView *)view attribute:(NSLayoutAttribute)attribute
{
    return [NSLayoutConstraint constraintWithItem:view
                                        attribute:attribute
                                        relatedBy:NSLayoutRelationEqual
                                           toItem:self
                                        attribute:attribute
                                       multiplier:1.0
                                         constant:0.0];
}

@end

#pragma mark - DZNWeakObjectContainer

@implementation DZNWeakObjectContainer

- (instancetype)initWithWeakObject:(id)object
{
    self = [super init];
    if (self) {
        _weakObject = object;
    }
    return self;
}

@end


@implementation EmptyConfiguer

+(instancetype)defaultConfiguer{
    
    EmptyConfiguer *loveLateAppConfiguer = [[EmptyConfiguer alloc] init];
    loveLateAppConfiguer.ySpace = 15;
    
    loveLateAppConfiguer.successImage = [UIImage imageNamed:@"late_no_data_icon"];
    loveLateAppConfiguer.failedImage = [UIImage imageNamed:@"late_no_data_icon"];
    
    loveLateAppConfiguer.successImage = [UIImage imageNamed:@"noData"];
    loveLateAppConfiguer.failedImage = [UIImage imageNamed:@"noData"];
    
    
    

    loveLateAppConfiguer.successTitle = @"暂无数据";
    loveLateAppConfiguer.failedTitle= @"暂无数据";
    return loveLateAppConfiguer;
    
}

@end

static const char emptyConfiguerKey= '\a';

@implementation UIScrollView (DznConfigurer)

#pragma mark  ------- DZNEmptyDataSet delegate

-(BOOL)emptyDataSetShouldDisplay:(UIScrollView *)scrollView{
    return YES;
}
- (BOOL)emptyDataSetShouldAllowTouch:(UIScrollView *)scrollView{
    
    return YES;
}
-(CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView{
    
    return self.configuer.yOffset;
}
-(CGFloat)spaceHeightForEmptyDataSet:(UIScrollView *)scrollView{
    return self.configuer.ySpace;
}

-(UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView{

    DznEmptyDataType loveLateAppType = self.configuer.emptyType;
    UIImage *loveLateAppImage = nil;
    if (loveLateAppType == DznEmptyDataTypeSuccess) loveLateAppImage = self.configuer.successImage;
    else if (loveLateAppType == DznEmptyDataTypeFailed) loveLateAppImage = self.configuer.failedImage;
    
    return loveLateAppImage;
}

-(NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView{
    
    NSDictionary *loveLateAppAttribute =@{NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue-Light" size:16.0],
                               NSForegroundColorAttributeName : [UIColor grayColor]};
    DznEmptyDataType loveLateAppType = self.configuer.emptyType;
    NSString *loveLateAppTitle = @"";
    if (loveLateAppType == DznEmptyDataTypeSuccess) loveLateAppTitle = self.configuer.successTitle;
    else if (loveLateAppType == DznEmptyDataTypeFailed) loveLateAppTitle = self.configuer.failedTitle;
    
    NSAttributedString *loveLateAppString = [[NSAttributedString alloc] initWithString:loveLateAppTitle attributes:loveLateAppAttribute];
    return loveLateAppString;
    
}

- (BOOL)emptyDataSetShouldAllowScroll:(UIScrollView *)scrollView{
    return YES;
}

#pragma mark ------- setter and getter
-(void)setConfiguer:(EmptyConfiguer *)configuer{
    objc_setAssociatedObject(self, &emptyConfiguerKey, configuer, OBJC_ASSOCIATION_RETAIN);
}

-(EmptyConfiguer *)configuer{
    
    EmptyConfiguer *loveLateAppConfiguer = objc_getAssociatedObject(self, &emptyConfiguerKey);
    if (!loveLateAppConfiguer) {
        loveLateAppConfiguer = [EmptyConfiguer defaultConfiguer];
        objc_setAssociatedObject(self, &emptyConfiguerKey, loveLateAppConfiguer, OBJC_ASSOCIATION_RETAIN);
    }
    return loveLateAppConfiguer;
}

@end

